package TCPClient;
import java.io.*;
import java.net.*;
import java.util.HashMap;
import java.util.Map;

import messangerTypes.*;

public class TCPClient
{
	private Socket socket;
	private TCPSendWorker sendWorker;
	private TCPReadWorker readWorker;
	private TCPClientMulticastReader mcastWorker;
	private TCPClientBaseUI baseUI;
	private User user;
	private Map <Integer, TCPClientUI> guis;
	
	//for debugging without GUI
	public TCPClient ()
	{
		try
		{        
			this.socket = new Socket("localhost", 6789);
			this.guis = new HashMap<Integer, TCPClientUI> ();
			
			sendWorker = new TCPSendWorker(socket.getOutputStream());
			readWorker = new TCPReadWorker(this, socket.getInputStream());
			mcastWorker = new TCPClientMulticastReader(this);
			
			mcastWorker.join();
			readWorker.join();
			sendWorker.join();
			
			mcastWorker.start();
			sendWorker.start();
			readWorker.start();
			
	        baseUI = new TCPClientBaseUI(this);
	        java.awt.EventQueue.invokeLater(baseUI);
	        baseUI.setVisible(true);
	        
	        Thread.sleep(500);
	        baseUI.clearFields();
			
		} catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void registerClientGUI (TCPClientUI ui, User user)
	{
		this.guis.put(user.getId(), ui);
	}
	
	public void removeClientGUI (User user)
	{
		this.guis.remove(user.getId());
	}
		
	public void close ()
	{
		sendWorker.quit = true;
		readWorker.quit = true;
		
		try
		{
			this.socket.close();
		} 
		catch (IOException e)
		{
			System.out.println("Client closed socket");
		}
	}
	
	synchronized public void writeMessage(String message, User destination) throws IOException
	{
		UserMessage userMessage = new UserMessage(this.user, destination, message);
		sendWorker.write(userMessage);		
	}
	
	//this is a call back for the workers
	synchronized public void processMessage (MessageBase mb)
	{
		System.out.println("TCPClient: process message "+ mb.toString());
		switch (mb.getType())
		{
		case CONNECT_REQUEST:
			user = ((ConnectRequest) mb).getRequestingUser();
			sendWorker.write(mb);
			break;
		case CONNECT_RESPONSE:
			ConnectResponse cr = (ConnectResponse) mb;

			if(cr.isLogin() == true)
				handleLogin(cr);
			else
				handleNewConverstation(cr);

			cr.toString();
			break;
		case KEEP_ALIVE:
			break;
		case MULTICAST_ANOUNCMENT:
			MultiCastAnouncement mca = (MultiCastAnouncement)mb;
			mca.removeUser(user);
			baseUI.addUsers(mca.getActiveUsers());
			break;
		case USER_MESSAGE:
			this.handleUSER_MESSAGE((UserMessage) mb);
			break;
			case NULL:
		default:
			break;
		}
	}
	
	private void handleLogin (ConnectResponse cr)
	{
		if(cr.getResponse() == ConnectResponse.ResponseCode.ACCEPTED)
		{
			this.user = cr.getRequestingUser();
		}
		else
		{
			this.user = null;
			this.baseUI.setLoggedOut();
		}
	}
	
	private void handleNewConverstation (ConnectResponse cr)
	{
		if(cr.getResponse() == ConnectResponse.ResponseCode.ACCEPTED)
		{
			//fire up the GUI
			TCPClientUI gui = new TCPClientUI(this, cr.getRequestedUser());
	        java.awt.EventQueue.invokeLater(gui);
	        gui.setVisible(true);       
	        
	        //register the gui with the client
	        this.registerClientGUI(gui, cr.getRequestedUser());
		}
	}
		
	private void handleUSER_MESSAGE (UserMessage message)
	{
		//get the GUI that message needs to be routed too
		TCPClientUI destination = this.guis.get(message.getSource().getId());
		//write the message to the GUI
		if(destination != null)
		{
			destination.write(message.getMessage());
		}
		else
		{
			//fire up the GUI
			TCPClientUI gui = new TCPClientUI(this, message.getSource());
	        java.awt.EventQueue.invokeLater(gui);
	        gui.setVisible(true);       
	        
	        //register the gui with the client
	        this.registerClientGUI(gui, message.getSource());
	        gui.write(message.getMessage());
		}
	}

	public User getUser()
	{
		return user;
	}
}